﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint;
using System.IO;
using Microsoft.SharePoint.Administration;
using System.Globalization;
using System.Xml.Linq;
using System.Collections;

namespace SharePoint.Contrib.Extensions
{
    /// <summary>
    /// Extension methods for the SPFeatureReceiverProperties class
    /// </summary>
    public static class SPFeatureReceiverPropertiesExtensions
    {
        /// <summary>
        /// Tries to cast the parent of the feature to a sitecollection. A return value indicates whether it succeeded or not.
        /// </summary>
        public static bool TryCastToSiteCollection(this SPFeatureReceiverProperties instance, out SPSite sitecollection)
        {
            sitecollection = instance.Feature.Parent as SPSite;
            return sitecollection != null;
        }

        /// <summary>
        /// Tries to cast the parent of the feature to a site (can also be rootweb). A return value indicates whether it succeeded or not.
        /// </summary>
        public static bool TryCastToSite(this SPFeatureReceiverProperties instance, out SPWeb site)
        {
            site = instance.Feature.Parent as SPWeb;

            if (site == null)
            { 
                SPSite sitecollection;
                if (instance.TryCastToSiteCollection(out sitecollection) == false)
                {
                    return false;
                }

                site = sitecollection.RootWeb;
            }

            return site != null;
        }

        /// <summary>
        /// Tries to cast the parent of the feature to a webapplication. A return value indicates whether it succeeded or not.
        /// </summary>
        public static bool TryCastToWebApplication(this SPFeatureReceiverProperties instance, out SPWebApplication application)
        {
            application = instance.Feature.Parent as SPWebApplication;
            return application != null;
        }

        /// <summary>
        /// Tries to cast the parent of the feature to a farm. A return value indicates whether it succeeded or not.
        /// </summary>
        public static bool TryCastToFarm(this SPFeatureReceiverProperties instance, out SPFarm farm)
        {
            farm = instance.Feature.Parent as SPFarm;
            return farm != null;
        }

        /// <summary>
        /// Returns a collection of feature Ids that will identify the activation dependencies for the feature.
        /// </summary>
        public static IEnumerable<Guid> ActivationDependenciesForFeature(this SPFeatureReceiverProperties instance)
        {
            var query = from SPFeatureDependency fd in instance.Definition.ActivationDependencies
                        let feature = SPFarm.Local.FeatureDefinitions[fd.FeatureId]
                        where feature.Hidden == true
                        select feature.Id;

            return query.ToArray();
        }

        /// <summary>
        /// Adds a new property to the feature
        /// </summary>
        /// <param name="property">The key and value pair to add</param>
        public static void AddFeatureProperty(this SPFeatureReceiverProperties instance, SPFeatureProperty property)
        {
            SPFeatureProperty found;
            if (instance.TryFindFeatureProperty(property.Name, out found))
            {
                found.Value = property.Value;
            }
            else
            {
                instance.Feature.Properties.Add(property);
            }

            instance.Feature.Properties.Update();
        }

        /// <summary>
        /// Tries to find a feature property. A return value indicates if the property was found.
        /// </summary>
        /// <param name="instance"></param>
        /// <param name="key">The property to find</param>
        public static bool TryFindFeatureProperty(this SPFeatureReceiverProperties instance, string key, out SPFeatureProperty property)
        {
            property = instance.Feature.Properties[key];
            return property != null;
        }

        /// <summary>
        /// Updates all file elements inside the module element, overriding the IgnoreIfAlreadyExist flag.
        /// </summary>
        public static void UpdateModule(this SPFeatureReceiverProperties instance)
        {

            var modules = (from SPElementDefinition element in instance.Feature.Definition.GetElementDefinitions(CultureInfo.CurrentCulture)
                           where element.ElementType == "Module"
                           let xmlns = XNamespace.Get(element.XmlDefinition.NamespaceURI)
                           let module = XElement.Parse(element.XmlDefinition.OuterXml)
                           select new
                           {
                               Url = module.Attribute("Url").OfType<string>(),
                               Path = Path.Combine(element.FeatureDefinition.RootDirectory, module.Attribute("Path").OfType<string>()),
                               Files = (from file in module.Elements(xmlns.GetName("File"))
                                        select new
                                        {
                                            Url = file.Attribute("Url").OfType<string>(),
                                            Name = file.Attribute("Name").OfType<string>(),
                                            Properties = (from property in file.Elements(xmlns.GetName("Property"))
                                                          select property).ToDictionary(
                                                                            n => n.Attribute("Name").OfType<string>(),
                                                                            v => v.Attribute("Value").OfType<string>())
                                        }).ToList()
                           }).ToList();

            using (SPWeb web = (instance.Feature.Parent as SPSite).OpenWeb())
            {
                modules.ForEach(module =>
                {
                    module.Files.ForEach(file =>
                    {
                        string hivePath = Path.Combine(module.Path, file.Url); // in RootFiles (aka HIVE)
                        string virtualPath = string.Empty;
                        if (string.IsNullOrEmpty(file.Name))
                        {
                            virtualPath = string.Concat(web.Url, "/", module.Url, "/", file.Url); // in MOSS
                        }
                        else
                        {
                            virtualPath = string.Concat(web.Url, "/", module.Url, "/", file.Name); // in MOSS
                        }


                        if (File.Exists(hivePath))
                        {
                            using (StreamReader sr = new StreamReader(hivePath))
                            {
                                SPFile virtualFile = web.GetFile(virtualPath);

                                bool CheckOutEnabled = virtualFile.Item.ParentList.ForceCheckout;
                                bool NeedsApproval = virtualFile.Item.ParentList.EnableModeration;

                                if (CheckOutEnabled)
                                {
                                    virtualFile.CheckOut();
                                }

                                virtualFile = web.Files.Add(virtualPath, sr.BaseStream, new Hashtable(file.Properties), true);

                                if (CheckOutEnabled)
                                {
                                    virtualFile.CheckIn("Updated", SPCheckinType.MajorCheckIn);
                                }

                                if (NeedsApproval)
                                {
                                    virtualFile.Approve("Updated");
                                }

                                virtualFile.Update();

                                if (virtualFile.CustomizedPageStatus == SPCustomizedPageStatus.Customized)
                                {
                                    virtualFile.RevertContentStream();
                                }
                            }
                        }
                    });
                });
            }

        }

    }
}
